﻿using System;
using System.Collections.Generic;
using System.Text;

using SchemaMonitor;
using Framework;

using System.Timers;
using System.IO;
using System.Net;
using System.Diagnostics;
using System.Web.Script.Serialization;
using SchemaMonitor.json;
using System.Threading;
using System.Reflection;
using SchemaMonitor.json.dict;
using SchemaMonitor.html;
using System.Data.SqlClient;
using System.Drawing;
using System.Data;
using System.Threading.Tasks;

namespace Monitor
{
    class Program
    {
        public const int PAGE_SIZE = 100; //Clients per thread to test every 60 mins
        public const double TIME_MS = 60 * 60 * 1000;



        static void Export()
		{


			CHtml.ExtractMessagesAndDump();

			foreach (var i in CHtml.AlbumIds())
				CHtml.ExtractPhotosAndDump(i);

			var posts = CHtml.ExtractPosts();
			CHtml.DumpPosts(posts);


			var dict = new Dictionary<ETimeline, List<CTimeline>>();
			foreach (var i in posts)
			{
				List<CTimeline> temp = null;
				if (!dict.TryGetValue(i.Type_, out temp))
				{
					temp = new List<CTimeline>();
					dict.Add(i.Type_, temp);
				}
				temp.Add(i);
			}
			Console.WriteLine("--------------------------");
			foreach (var i in dict.Keys)
				Console.WriteLine(CUtilities.NameAndCount(i.ToString(), dict[i]));
		}

		static void ImportChapters()
		{




			var remoteFB = new CWebSrcBinary("http://recyclebin.mikeconnellministries.com/webservices/FBDatasrc.aspx");
			var remotePH = new CWebSrcBinary("http://recyclebin.mikeconnellministries.com/datasrc.aspx");
			var localPH = CConfig.Photos; ;
			var localFB = (CSqlClient)CDataSrc.Default;

			var u = new SchemaMonitor.CUser(remotePH, 1);
			u.All = new SchemaMonitor.CNode(remotePH).SelectAll();
			var nodes = u.All.GetByTypeId(149);
			foreach (var i in nodes)
			{
				var r = i.Root;
				var p = new CSoftPost(r, u);
				var s = p.Message + p.Description;
			}



			var fb = new string[] { CNodeLabels_OneToOne.TABLE_NAME, CNodeLabel.TABLE_NAME };
			var local = (CSqlClient)localFB;
			var remote = remotePH;

			var cn = (SqlConnection)local.Connection();
			var tx = cn.BeginTransaction();
			try
			{
				if (false)
				foreach (var tbl in fb)
				{
					if (null == tx)
						tx = cn.BeginTransaction();

					var dt = remote.SelectAll_Dataset(tbl).Tables[0];
					local.DeleteAll(tbl, tx);
					
					local.SetIdentityInsert(true, tbl, tx);

					var copy = new System.Data.SqlClient.SqlBulkCopy(cn, SqlBulkCopyOptions.KeepIdentity, tx);
					copy.DestinationTableName = tbl;
					if (CConfig.CommandTimeoutSecs() > 0)
						copy.BulkCopyTimeout = CConfig.CommandTimeoutSecs();
					copy.WriteToServer(dt);

					local.SetIdentityInsert(false, tbl, tx);

					tx.Commit();
					tx = null;
				}
			}
			catch (Exception ex)
			{
				tx.Rollback();
				throw ex;
			}
			finally
			{
				cn.Close();
			}
			//Console.ReadLine();

			fb = new string[] { CTagType.TABLE_NAME, CMetaTag.TABLE_NAME, CLabel.TABLE_NAME, CLabel_OneToOne.TABLE_NAME };
			local = (CSqlClient)localPH;
			remote = remoteFB;
			cn = (SqlConnection)local.Connection();
			tx = cn.BeginTransaction();
			try
			{
				foreach (var tbl in fb)
				{
					if (null == tx)
						tx = cn.BeginTransaction();

					var dt = remote.SelectAll_Dataset(tbl).Tables[0];
					local.DeleteAll(tbl, tx);
					if (tbl == CTagType.TABLE_NAME || tbl == CMetaTag.TABLE_NAME)
					{
						local.SetIdentityInsert(true, tbl, tx);

						var copy = new System.Data.SqlClient.SqlBulkCopy(cn, SqlBulkCopyOptions.KeepIdentity, tx);
						copy.DestinationTableName = tbl;
						if (CConfig.CommandTimeoutSecs() > 0)
							copy.BulkCopyTimeout = CConfig.CommandTimeoutSecs();
						copy.WriteToServer(dt);

						local.SetIdentityInsert(false, tbl, tx);
					}
					else
						local.BulkInsertWithTx(dt, tbl, tx);
					tx.Commit();
					tx = null;
				}
			}
			catch (Exception ex)
			{
				tx.Rollback();
				throw ex;
			}
			finally
			{
				cn.Close();
			}


			Console.ReadLine();


			//CConfig.Photos = remote;
			//CDataSrc.Default = remote2;

			var d1 = DateTime.Now;
			//var guids = remote.MakeListGuid("select PhotoMd5 from " + CPhotoHash.TABLE_NAME);
			var pi = new CPagingInfo(200);
			pi.SortByColumn = "PhotoMD5";
			var dr = local.Local.ExecuteReader("SELECT PhotoMd5, PhotoBlob FROM " + CPhotoHash.TABLE_NAME);

			//var md5_pk = new Dictionary<Guid, long>();
			var dict_Len = new Dictionary<long, List<Guid>>();
			var dict_size = new Dictionary<long, List<Guid>>();
			int count = 0;
			while (dr.Read())
			{
				var x = DateTime.Now;

				var g = CAdoData.GetGuid(dr, 0);
				var b = CAdoData.GetBytes(dr, 1);
				var y = DateTime.Now;
				var t1 = y.Subtract(x);

				var z = DateTime.Now;
				var t2 = z.Subtract(y);

				List<Guid> gg = null;
				if (!dict_Len.TryGetValue(b.Length, out gg))
				{
					gg = new List<Guid>();
					dict_Len.Add(b.Length, gg);
				}
				gg.Add(g);

				Image bmp = null;
				try
				{
					bmp = Bitmap.FromStream(new MemoryStream(b));
				}
				catch
				{
					Console.WriteLine(g.ToString() + " - could not read width/height");
					continue;
				}
				var size = bmp.Size;
				uint u1 = (uint)size.Width;
				uint u2 = (uint)size.Height;
				ulong i64 = (((ulong)u1) << 32) | u2;
				long key = (long)i64;
				bmp.Dispose();

				gg = null;
				if (!dict_size.TryGetValue(key, out gg))
				{
					gg = new List<Guid>();
					dict_size.Add(key,gg);
				}
				gg.Add(g);

				count++;
				if (count % 200 == 0)
					Console.WriteLine(count.ToString("n0"));
			}
			dr.Close();

			File.WriteAllBytes(@"c:\_dump_photos\dict_size_long_listOfGuid_fromDb.gz", CBinary.Zip(CProto.Serialise(dict_size)));
			File.WriteAllBytes(@"c:\_dump_photos\dict_len_long_listOfGuid_fromDb.gz", CBinary.Zip(CProto.Serialise(dict_Len)));



			var d2 = DateTime.Now;
			Console.WriteLine(CUtilities.Timespan(d2.Subtract(d1)) + " loaded ids");
			var files = System.IO.Directory.GetFiles(@"D:\_dump_photos");
			var d3 = DateTime.Now;
			Console.WriteLine(CUtilities.Timespan(d3.Subtract(d2)) + " read files ");
			//Console.WriteLine(CUtilities.NameAndCount("db", guids));
			Console.WriteLine(CUtilities.NameAndCount("fs", files));

			var matched = new Dictionary<string, Guid>();
			var len_mat = new Dictionary<long, List<Guid>>();
			var siz_mat = new Dictionary<long, List<Guid>>();
			var dbl_mat = new Dictionary<long, List<Guid>>();

			var dict_Len2 = new Dictionary<long, List<string>>();
			var dict_size2 = new Dictionary<long, List<string>>();

			var ff = new List<string>(files);
			foreach (var i in files)
			{
				var idx = ff.IndexOf(i) + 1;
				var s = string.Concat(idx, " of ", ff.Count, " = ", CTextbox.SetPercent(100.0 * idx / ff.Count), "\t", CUtilities.Timespan(DateTime.Now.Subtract(d3)), "\t");
				var bb = File.ReadAllBytes(i);
				var md5 = CBinary.MD5_(File.ReadAllBytes(i));

				List<string> ss = null;
				if (!dict_Len2.TryGetValue(bb.Length, out ss))
				{
					ss = new List<string>();
					dict_Len2.Add(bb.Length, ss);
				}
				ss.Add(i);



				Image bmp = null;

				try
				{
					bmp = Bitmap.FromStream(new MemoryStream(bb));
				}
				catch
				{
					Console.WriteLine(Path.GetFileName(i) + " - could not read width/height");
					continue;
				}
				var size = bmp.Size;
				uint u1 = (uint)size.Width;
				uint u2 = (uint)size.Height;
				ulong i64 = (((ulong)u1) << 32) | u2;
				long key = (long)i64;
				bmp.Dispose();

				ss = null;
				if (!dict_size2.TryGetValue(key, out ss))
				{
					ss = new List<string>();
					dict_size2.Add(key, ss);
				}
				ss.Add(i);
			}


			File.WriteAllBytes(@"D:\_dump_photos\dict_size_long_listOfGuid_fileSys.gz", CBinary.Zip(CProto.Serialise(dict_size2)));
			File.WriteAllBytes(@"D:\_dump_photos\dict_len_long_listOfGuid_fileSys.gz", CBinary.Zip(CProto.Serialise(dict_Len2)));


			var matchOnLen = new Dictionary<Guid, List<string>>();
			foreach (var i in dict_Len)
			{
				var len1 = i.Key;
				var guids1 = i.Value;

				if (dict_Len2.ContainsKey(len1))
				{
					var strings = dict_Len2[len1];
					//Store the matches
					foreach (var j in guids1)
					{
						List<string> ss = null;
						if (!matchOnLen.ContainsKey(j))
						{
							ss = new List<string>();
							matchOnLen.Add(j, ss);
						}
						ss.AddRange(strings);
					}
				}
			}
			Console.WriteLine(CUtilities.NameAndCount("Len-Matches", matchOnLen.Count));


			var matchOnSize = new Dictionary<Guid, List<string>>();
			foreach (var i in dict_size)
			{
				var size1 = i.Key;
				var guids1 = i.Value;

				if (dict_size2.ContainsKey(size1))
				{
					var strings = dict_size2[size1];
					//Store the matches
					foreach (var j in guids1)
					{
						List<string> ss = null;
						if (!matchOnSize.ContainsKey(j))
						{
							ss = new List<string>();
							matchOnSize.Add(j, ss);
						}
						ss.AddRange(strings);
					}
				}
			}
			Console.WriteLine(CUtilities.NameAndCount("Size-Matches", matchOnSize.Count));


			var doubleMatch = new Dictionary<Guid, List<string>>();
			foreach (var guid in matchOnLen.Keys)
			{
				List<string> stringsOnLen = null;
				if (!matchOnSize.TryGetValue(guid, out stringsOnLen))
					continue;

				List<string> stringsOnSize = null;
				if (!matchOnSize.TryGetValue(guid, out stringsOnSize))
					continue;

				var temp = new List<string>();
				foreach (var i in stringsOnLen)
					if (stringsOnSize.Contains(i))
						temp.Add(i);

				if (temp.Count > 0)
					doubleMatch.Add(guid, temp);
			}
			Console.WriteLine(CUtilities.NameAndCount("Dbl-Matches", doubleMatch.Count));
			//Console.ReadLine();

			var m1 = CBinary.Zip(CProto.Serialise(matchOnLen));
			var m2 = CBinary.Zip(CProto.Serialise(matchOnSize));
			var m3 = CBinary.Zip(CProto.Serialise(doubleMatch));

			File.WriteAllBytes(@"D:\_dump_photos\matchOnLen_guid_listOfString.gz", m1);
			File.WriteAllBytes(@"D:\_dump_photos\matchOnSize_guid_listOfString.gz", m2);
			File.WriteAllBytes(@"D:\_dump_photos\matchOnBoth_guid_listOfString.gz", m3);

			m1 = m2 = m3 = null;
			Console.ReadLine();

			Console.WriteLine("Finished: " + CUtilities.CountSummary(matched.Count, "match"));
			var names = new List<string>(matched.Keys);
			var vals = new List<Guid>(matched.Values);
			File.WriteAllLines(@"D:\_dump_photos\__names.txt", names);
			names.Clear();
			foreach (var i in vals)
				names.Add(i.ToString());
			File.WriteAllLines(@"D:\_dump_photos\__vals.txt", names);
			Console.ReadLine();

			//var u = new CUser(1);
			var albums = CTagType.Cache.FB_Album;
			//albums.MetaTags.DeleteAll();

			/*
			var posts = u.All.GetByTypeId(149);
			var list = new List<string>();

			var pix = new CPhotoList();
			var hashs = new CPhotoHashList();
			var cps = new CChapterPictureList();
			foreach (var i in posts)
			{
				var sp = new CSoftPost(i.Root, u);
				if (sp.HasAttachments)
				foreach (var j in sp.Attachments_)
				{
					if (j.Description.Length > 0 || j.Title.Length > 0)
					{
						string caption = j.Description.Length > 0 ? j.Description : j.Title;
						if (null != j.Target)
						{
							var pic = SchemaMonitor.CPhoto.Cache.GetById(j.Target.Value);
							if (null != pic)
							{
								if (null == pic.PhotoHash.PhotoCaptions)
									pic.PhotoHash.PhotoCaptions = caption;
								else
									pic.PhotoHash.PhotoCaptions += "\r\n\r\n" + caption;

								pic.Caption = caption;
								pix.Add(pic);
								hashs.Add(pic.PhotoHash);
								//pic.Save();
								//pic.PhotoHash.Save();
								foreach (var k in pic.PhotoHash.ChapterPictures)
								{
									k.PictureCaptions = pic.PhotoHash.PhotoCaptions;
									//k.Save();
									cps.Add(k);
								}
								Console.WriteLine(caption);
							}
						}
					}
					else if (j.Type != "photo")
						list.Add(j.Type);
				}
			}
			pix.SaveAll();
			hashs.SaveAll();
			cps.SaveAll();
			*/

			var pics = u.All.GetByTypeId(152);// new SchemaMonitor.CNode().SelectByTypeId(152);
			foreach (var i in pics)
			{
				var r = i.Root;
				var id = long.Parse(r.Id);
				var p = SchemaMonitor.CPhoto.Cache.GetById(id);
				foreach (var f in r.Fields)
				{
					switch (f.Tag.TagName)
					{

						case "height":
						case "width":
							var ph = p.PhotoHash;
							if (ph.PhotoHeight == int.MinValue)
							{
								ph.PhotoHeight = int.Parse(r.Fields["height"].AsString);
								ph.Save();
							}
							if (ph.PhotoWidth == int.MinValue)
							{
								ph.PhotoWidth = int.Parse(r.Fields["width"].AsString);
								ph.Save();
							}
							break;

						case "id":
						case "url":
						case "comments":
						case "created_time":
						case "updated_time":
						case "from":
						case "icon":
						case "images":
						case "link":
						case "picture":
						case "source":
						case "likes":
							break;
						case "album":
							var album = f.AsPointer(u.All);
							var name = album.Root.Name;
							var index = name.IndexOf(" (");
							if (-1 != index)
								name = name.Substring(0, index);

							if (name == "Mobile Uploads")
								continue;

							var tag = CMetaTag.Cache.GetOrCreate(name, albums.TypeId);

							if (null == p)
								p = SchemaMonitor.CPhoto.Cache.GetById(r.Id2);
							if (null == p)
								p = SchemaMonitor.CPhoto.Cache.GetById(r.Id1);
							if (null == p)
								continue;
							if (!p.PhotoHash.Labels.MetaTagIds.Contains(tag.TagId))
							{
								var label = new CLabel();
								label.LabelTagId = tag.TagId;
								label.LabelGuid = p.Md5;
								label.Save();
							}

							break;

						default:
							Console.WriteLine(r.Id + " " + f.Tag.TagName + " " + f.AsString);
							break;
					}
				}
			}
			return;



			/*
			var rids = new CPhotoHash(remote).SelectDistinctGuid("PhotoMd5");
				var lids = new CPhotoHash().SelectDistinctGuid("PhotoMd5");
				var toGet = new List<Guid>();
				foreach (var i in rids)
					if (!lids.Contains(i))
						toGet.Add(i);
				rids = null;
				lids = null;
				var pi = new CPagingInfo(25);
				pi.TableName = CPhotoHash.TABLE_NAME;
				var pg = CUtilities.Page(toGet, pi.PageSize, pi.PageIndex);
				while (pg.Count > 0)
				{
					Console.WriteLine(string.Concat("Downloading ", pg.Count, " of ", toGet.Count, ", from #", pi.Offset));
					var ds = new CPhotoHash(remote).SelectWhere_Dataset(new CCriteriaList("PhotoMd5", ESign.IN, pg), CPhotoHash.TABLE_NAME);
					var temp = new CPhotoHash().MakeList(ds);
					foreach (var i in temp)
						new CPhotoHash(i).Save();
					pi.PageIndex += 1;
					pg = CUtilities.Page(toGet, pi.PageSize, pi.PageIndex);
				}
				*/




			/*
			var tbls = new List<string>();
			var tbls_ident = new List<string>();
			//tbls.Add(SchemaMonitor.CPhoto.TABLE_NAME);
			//tbls.Add(SchemaMonitor.CNode.TABLE_NAME);
			//tbls.Add(SchemaMonitor.CNodesArchived.TABLE_NAME);
			//tbls.Add(SchemaMonitor.CSignature.TABLE_NAME);

			tbls_ident.Add(SchemaMonitor.CBook.TABLE_NAME);
			tbls_ident.Add(SchemaMonitor.CSection.TABLE_NAME);
			tbls_ident.Add(SchemaMonitor.CChapter.TABLE_NAME);
			tbls_ident.Add(SchemaMonitor.CChapterPicture.TABLE_NAME);
			tbls_ident.Add(SchemaMonitor.CSchema.TABLE_NAME);
			tbls_ident.Add(SchemaMonitor.CType.TABLE_NAME);
			tbls_ident.Add(SchemaMonitor.CTag.TABLE_NAME);
			tbls.AddRange(tbls_ident);

			foreach (var tbl in tbls)
			{
				try
				{
					var db = CConfig.Photos;

					Console.WriteLine();
					Console.WriteLine("Getting: " + tbl);
					var d = DateTime.Now;
					var ds = new System.Data.DataSet();
					try
					{ ds = remote.SelectAll_Dataset(tbl); }
					catch
					{
						d = DateTime.Now;
						db = CDataSrc.Default;
						ds = remote2.SelectAll_Dataset(tbl);
					}
					var dt = ds.Tables[0];
					var d2 = DateTime.Now;
					var t = d2.Subtract(d);
					Console.WriteLine("Got " + dt.Rows.Count.ToString("n0") + " in " + CUtilities.Timespan(t));

					var count = db.SelectCount(tbl, null);
					if (! tbls_ident.Contains(tbl))
						if (count == dt.Rows.Count)
						{
							Console.WriteLine("Skipped: " + tbl + " " + count.ToString("n0"));
							continue;
						}

					if (count > 0)
					{
						Console.WriteLine("Deleting " + count.ToString("n0"));
						db.DeleteAll(tbl, null);
					}

					if (tbls_ident.Contains(tbl))
					{
						Console.WriteLine("Inserting again (with tx)...");

						var cn = (SqlConnection)db.Local.Connection();
						var tx = cn.BeginTransaction();
						try
						{
							CSqlClient dbs = (CSqlClient)db;

							var copy = new SqlBulkCopy(tx.Connection, SqlBulkCopyOptions.KeepIdentity, tx);
							copy.DestinationTableName = tbl;
							if (CConfigBase.CommandTimeoutSecs() > 0)
								copy.BulkCopyTimeout = CConfigBase.CommandTimeoutSecs();


							//if (null != mappings)
							//	foreach (int i in mappings.Keys)
							//		copy.ColumnMappings.Add(i, mappings(i));

							for (var i = 0; i < dt.Columns.Count; i++)
								copy.ColumnMappings.Add(i, i);


							dbs.SetIdentityInsert(true, tbl, tx);
							copy.WriteToServer(dt);
							dbs.SetIdentityInsert(false, tbl, tx);

							d = DateTime.Now;
							t = d.Subtract(d2);
							Console.WriteLine("Inserted " + dt.Rows.Count.ToString("n0") + " in " + CUtilities.Timespan(t));

							tx.Commit();
						}
						catch
						{
							tx.Rollback();
							throw;
						}
						finally
						{
							cn.Close();
						}

					}
					else
					{
						Console.WriteLine();
						Console.WriteLine("Inserting...");
						db.BulkInsert(dt, tbl);
						d = DateTime.Now;
						t = d.Subtract(d2);
						Console.WriteLine("Inserted " + dt.Rows.Count.ToString("n0") + " in " + CUtilities.Timespan(t));
					}

				}
				catch (Exception ex)
				{
					Console.WriteLine();
					Console.WriteLine("ERROR: " + ex.Message);
					Console.ReadLine();
				}
			}
			Console.WriteLine("Done");
			Console.ReadLine();
			*/
			
			return;



			string path = System.Environment.CurrentDirectory + "\\all.bin.gz";
			try
			{
				u.All = (CNodeList)CBinary.Unpack(File.ReadAllBytes(path));
			}
			catch
			{
				var all = u.All;
				File.WriteAllBytes(path, CBinary.Pack(all));
			}
			 path = System.Environment.CurrentDirectory + "\\photohash.bin.gz";
			try
			{
				CWebSrc b = (CWebSrcBinary)CConfig.Photos;
				CPhotoHash.Cache = (CPhotoHashList)CBinary.Unpack(File.ReadAllBytes(path));
			}
			catch
			{
				File.WriteAllBytes(path, CBinary.Pack(CPhotoHash.Cache));
			}


			foreach (var c in CChapter.Cache)
			{
				var n = c.Node_(u);
				var r = n.Root;
				var post = new CSoftPost(r, u);
				if (null == c.ChapterText || DateTime.MinValue == c.ChapterCreated || c.ChapterText != post.Message)
				{
					c.ChapterText = post.Message;
					c.ChapterCreated = post.CreatedTime;					
					c.Save();
					Console.WriteLine(string.Concat("Updated Chapter #", CChapter.Cache.IndexOf(c) + 1, " of ", CChapter.Cache.Count));
				}

				var pp = SchemaMonitor.CPhoto.Cache.GetByIds(post.PhotoIds);
				var cc = new CChapterPictureList();
				foreach (var p in pp)
				{
					if (null == CPhotoHash.Cache.GetById(p.Md5))
					{
						var cpp2 = c.ChapterPictures.GetByPhotoMD5(p.Md5);
						foreach (var i in cpp2)
						{
							Console.WriteLine(string.Concat("Deleted MISSING pic ", c.ChapterPictures.IndexOf(i) + 1, " of ", c.ChapterPictures.Count));
							i.Delete();
						}
						continue;
					}
					var cpp = c.ChapterPictures.GetByPhotoMD5(p.Md5);
					if (cpp.Count == 0)
					{
						var cp = new CChapterPicture();
						cp.PictureChapterId = c.ChapterId;
						cp.PicturePhotoMD5 = p.Md5;
						cp.PictureSortOrder = pp.IndexOf(p);
						


						//cp.Save();
						cc.Add(cp);

						//Console.WriteLine(string.Concat("Storing photo #", pp.IndexOf(p) + 1, " of ", pp.Count, " for Chapter ", c.ChapterName, " in ", c.Section.SectionName));
					}
				}
				if (c.ChapterPictures.Count + cc.Count != pp.Count)
				{
					foreach (var j in c.ChapterPictures)
						if (cc.GetByPhotoMD5(j.PicturePhotoMD5).Count == 0)
						{
							j.Delete();
							Console.WriteLine(string.Concat("Deleted pic ", c.ChapterPictures.IndexOf(j) + 1, " of ", c.ChapterPictures.Count));
						}
					//Console.WriteLine(string.Concat(pp.Count, " != ", c.ChapterPictures.Count, " for ", c.ChapterName));
					//c.ChapterPictures.DeleteAll();
					//CDataSrc.Default.BulkSaveDelete(null, c.ChapterPictures);
					//Console.WriteLine(string.Concat("Deleted ", c.ChapterPictures.Count, " photos for Chapter #", CChapter.Cache.IndexOf(c) + 1, " of ", CChapter.Cache.Count));
				}
				if (cc.Count > 0)
				{
					Console.WriteLine(string.Concat("Updating ",cc.Count, " photos for Chapter #", CChapter.Cache.IndexOf(c) + 1, " of ", CChapter.Cache.Count));
					cc.SaveAll();
					Thread.Sleep(1000);
					//CDataSrc.Default.BulkSaveDelete(cc, null);
				}
			}
		}
		static void Import_Posts(CUser u)
		{
			var posts = CHtml.LoadPosts();
			posts.Reverse();

			var typeComment = CType.Cache.GetBySig("post");
			foreach (var i in posts.Index)
			{
				switch (i.Key)
				{
					case ETimeline.PostedVideoTimeline:
					case ETimeline.PostPics:
					case ETimeline.PostText:
					case ETimeline.StatusUpdate:
						foreach (var j in i.Value)
						{
							if (string.IsNullOrEmpty(j.Comment))
								continue;

							var n = new CNodeRoot(typeComment);
							n.Set("message", j.Comment, u);
							n.Set("created_time", j.Date, u);
							n.Set("story", j.Intro, u);
							n.IdBase64 = string.Concat(j.Comment, j.Date, j.Intro);
							n.Persist(u);
						}
						break;

					default:
						foreach (var j in i.Value)
							Console.WriteLine(i.Key + ": " + j.Intro + " ** " + j.Comment);
						break;
				}
			}
		}
		static void Import_Photos(CUser u)
		{
			var photos = new List<SchemaMonitor.html.CPhoto>();
			foreach (var i in CHtml.AlbumIds())
			{
				var temp = CHtml.LoadPhotos(i);
				photos.AddRange(temp);
			}
			photos.Reverse();

			var type = CType.Cache.GetBySig("photo");
			var tag = type.Tags.GetByName("comments");
			foreach (var i in photos)
			{
				var n = new CNodeRoot(type);
				n.Set("id", i.Id, u);
				n.Set("created_time", i.Date, u);
				n.Set("picture", i.FileName, u);    //name

				if (null != i.Comments && 0 != i.Comments.Count)
				{
					var list = new List<Guid>();
					foreach (var j in i.Comments)
						list.Add(Comment(j, u));
					n.Fields.Add(new CFieldPointer(n, tag, list));
				}
				n.Persist(u);
			}
		}
		static Guid Comment(CComment c, CUser u)
		{
			var type = CType.Cache.GetBySig("comment");
			var n = new CNodeRoot(type);
			n.Set("created_time", c.Date, u);
			n.Set("message", c.Comment, u);
			n.Set("name", c.User, u);
			n.IdBase64 = string.Concat(c.Comment, c.Date, c.User);
			var nn = n.Persist(u);
			return nn.NodeGuid;
		}


		#region "Old logic"
		/*

				/*
				var ws = (CWebSrcBinary)SchemaMonitor.CConfig.Photos;
				ws.NO_ASYNC = false;
				var p = SchemaMonitor.CPhoto.PutPhotos(u.UserId);
				Console.ReadLine();


				var typePhoto = CType.Cache.GetBySig("photo");
				var typePost = CType.Cache.GetBySig("post");

				var photos = u.All.GetByTypeId(typePhoto.TypeId);
				var posts = u.All.GetByTypeId(typePost.TypeId);

				var idxPhot = photos.IndexByTicks;
				var idxPost = posts.IndexByTicks;

				var keysPot = new List<long>(idxPost.Keys);
				var keysPhot = new List<long>(idxPhot.Keys);
				keysPhot.Sort();
				keysPost.Sort();
				var intersect = new List<long>();
				foreach (var i in keysPhot)
					if (keysPost.Contains(i))
						if (!intersect.Contains(i))
							intersect.Add(i);

				//Consider exact intersections first
				foreach (var i in intersect)
				{
					var iPost = idxPost[i];
					var iPhot = idxPhot[i];
					var d = new DateTime(i);
					var sPost = string.Empty;
					if (iPost.Count == 1)
						sPost = iPost[0].Root.Name;
					else
						sPost = CUtilities.CountSummary(iPost, "posts");
					Console.WriteLine(string.Concat(CUtilities.Timespan(d), "\t", sPost, " = ", CUtilities.CountSummary(iPhot, "photo")));
				}

				int ptr = 0;
				long phot = 0;
				long last = phot;
				foreach (var i in keysPost)
				{
					phot = keysPhot[ptr];
					while (phot < i)
					{
						ptr += 1;
						last = phot;
						if (ptr < keysPhot.Count)
							phot = keysPhot[ptr];
						else
							break;
					}
					if (last == 0)
						last = phot;

					var oneOrMore = idxPost[i];
					var before = idxPhot[last];

					var after = idxPhot[phot];
					var s = CUtilities.CountSummary(oneOrMore, "posts", "none");
					if (oneOrMore.Count == 1)
						s = oneOrMore[0].Root.Name;
					s = CUtilities.Truncate(s);
					var d1 = CUtilities.Timespan( new DateTime(i));
					var d2 = CUtilities.Timespan(new DateTime(last));
					var d3 = CUtilities.Timespan(new DateTime(phot));
					Console.WriteLine(String.Concat(s, ": before=", before.Count, ", after=", after.Count, " ", last == phot ? "same": "diff"));
				}
					

				Console.ReadLine();

				Import_Posts(u);
				Import_Photos(u);
				Console.ReadLine();
				*/ 
		# endregion



		static void FudgeToken(CUser u)
		{
			//Store token
			u.UserToken = "EAACEdEose0cBALcrrPyZA9I8lcLn0OOFDwY0jS8ebVkcoOS6arZCHTBtZCbEcTQJLZCkrket35YrHjju24JgRjGivO6RXIkYQKnj52AQqmjuzTtuIVko4xNFV2owZCEgs1awV2f5ZBtqGwFAEtzeu1IMdqLQRhaKjEEgMQACJUoD7bEIui4OnKv7rLjF2SwH4ZD";
			u.UserTokenExpires = DateTime.Now.AddHours(3);
			u.UserIsActive = true;
			u.Save();

			Console.WriteLine("User: " + u.UserName);
		}
		static CUser FudgeUser()
		{
			var u = new CUser();
			var uu = u.SelectAll();
			Console.WriteLine("Users: " + uu.Count.ToString());
			if (uu.Count == 0)
			{
				u = new CUser();
				u.UserName = "Jeremy";
			}
			else if (uu.GetByName("Jeremy").Count > 0)
				u = uu.GetByName("Jeremy")[0];
			else
				u = uu[0];
			return u;
		}

		//Launch timer, run continuously
		static void Main(string[] args)
        {
			if (AlreadyRunning())
                return;
            Console.WriteLine("starting");

            ///TEMP 
            try
            {
				var u = FudgeUser();

                /*
                var dictRecip = new Dictionary<long, string>();
                var threads = CHtml.ExtractMessages(@"C:/Dev/facebook-100014244433824/html/messages.htm");
                var nodes = new List<CNodeRoot>();
                var type = CType.Cache.GetById(200);
                foreach (var i in threads)
                {
                    var names = i.SetUserNames(u, dictRecip);
                    var dict = i.AsDict();


                    var node = new CNodeRoot(dict, type, u);
                    nodes.Add(node);
                }
                foreach (var i in nodes)
                    i.Persist(u);
                */


                //FudgeToken(u);

                try
                {
                    CEngine.Process(u);
                }
                catch (Exception ex2)
                {
                    CLog.Log(ex2, u);
                }
            }
            catch (Exception ex)
            {
                CLog.Log(ex, new CUser());
            }

            Console.WriteLine("Finished");
            Console.ReadLine();
            return;



            //Start the timer
            CLog.Log_("Started - running every ", CUtilities.TimespanMs(TIME_MS));
            var t = new System.Timers.Timer(TIME_MS);
            t.AutoReset = true;
            t.Elapsed += new ElapsedEventHandler(t_Elapsed);
            t.Start();

            //Wait until kill file spotted
            string filePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\die.txt";
            while (! File.Exists(filePath))
                System.Threading.Thread.Sleep(10000);
            t.Stop();
            CLog.Log("Controlled exit");
        }

        #region Update Data for Client
        static void t_Elapsed(object sender, ElapsedEventArgs e)
        {
            var l = new CLog();

            //paging
            CPagingInfo pi = new CPagingInfo(PAGE_SIZE, 0, CUser.PRIMARY_KEY_NAME);

            //Get pages of users
            CUser u = new CUser();
            CUserList page = u.SelectByIsActive(pi, true);
            l.Log(CUtilities.CountSummary(pi.Count, "user"), " to check");

            //run each page in a separate thread
            while (page.Count > 0)
            {
                //Process Current Page (TODO: WCF)
                CEngine.ProcessInThread(page);
                pi.PageIndex += 1;

                //Get Next Page
                page = u.SelectByIsActive(pi, true);
                l.Log("Page #", 1 + pi.PageIndex, " queued");

                //Take a breath every 10 pages (10 threads of 100 users to check)
                if (pi.PageIndex % 10 == 0)
                    Thread.Sleep(5000); //5 secs
            }
        }
        #endregion

        #region AlreadyRunning
        private static bool AlreadyRunning()
        {
            try
            {
                Process p = Process.GetCurrentProcess();
                foreach (Process i in Process.GetProcessesByName(p.ProcessName))
                    if (i.Id != p.Id)
                        if (i.MainModule.FileName.ToLower() == p.MainModule.FileName.ToLower())
                        {
                            CLog.Log("Error: process is already running - exitting!");
                            return true;
                        }
            }
            catch (Exception ex)
            {
                CLog.Log_("Failed to check for processes already running: ", ex.Message);
            }
            return false;
        }
        #endregion

        //Logging

    }
}



/*

var d = new Dictionary<string, object>();
d.Add("1", "asdf");
d.Add("2", DateTime.Now);
d.Add("3", int.MinValue);
d.Add("4", double.MaxValue);
d.Add("5", true);

var b1 = CSerialise.Serialise(d, ESerialisation.BinarySer);
var b2 = CSerialise.DC.Serialise_(d);

var gz1 = CBinary.Zip(b1);
var gz2 = CBinary.Zip(b2);

var d1 = CSerialise.Deserialise<Dictionary<string, object>>(b1, ESerialisation.BinarySer);
var d2 = CSerialise.DC.Deserialise_<Dictionary<string, object>>(b2);

Console.ReadLine();




var tag = new CTag();
var f1 = new CFieldString(tag, "asdf");
var f2 = new CFieldInt(tag, 123);
var list = new CFieldList();
list.Add(f1);
list.Add(f2);

var bb = CSerialise.Proto.Serialise_(list);
list = CSerialise.Proto.Deserialise_<CFieldList>(bb);
Console.ReadLine();

*/
